package com.foreveross.crawl.adapter.sub.impl20140402.v3.tianxun;

import java.util.List;
import java.util.Map;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.hadoop.hbase.util.Strings;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;

import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.adapter.sub.impl20140402.v3.TianxunAdapter;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.AgentEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;

/**
 * 天巡适配器。<br/>
 * 
 * 请求的数据格式为Json。
 * 
 * @author luomingliang@foreveross.com
 * @version 1.0.0
 * @date 2014-07-08
 */
@SuppressWarnings({ "unchecked", "deprecation" })
public class TianxunInterAdapter extends TianxunAdapter {

	/** ******************************** PARAMS *********************************************** */

	private final static boolean flightNotNULL = false; // 是否过滤没有航班号的航班 。
	private final static int CABIN_RETRY = 3; // 舱位加载失败重试次数。
	private final static int COOKIE_RETRY = 3;// Cookie信息加载失败重试次数。
	private final static int DETAIL_RETRY = 1;// 详细数据加载失败重试次数。
	private final static long FETCH_SLEEP_TIME = 200; // 详细数据抓取间隔时间。
	private final static boolean IS_FILTER_CARRIER = false; // 是否过滤指定的航空公司。
	private final static int MAX_TRANSIT = 1; // 抓取的最多的中转次数。[0：直飞，1：1次中转，2：2次中转]（不过滤 设置为-1）

	private final static String BASEURL = "http://www.tianxun.cn/transport/flights/";
	private final static String DATAURL = "http://www.tianxun.cn/dataservices/routedate/v2.0/";
	private final static Map<String, String> CABINS = ImmutableMap.of("Economy", "经济舱"/*, "PremiumEconomy", "高端经济舱", "Business", "商务舱", "First", "头等舱"*/);

	/** ******************************** Constractors *********************************************** */

	public TianxunInterAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	/** ******************************** Override *********************************************** */

	/** ******************************** ONEWAY AND ROUNDS *********************************************** */

	/** 解析国际往返。 */
	public Object parseInterRound(Object obj) throws Exception {
		List<AbstractPlaneInfoEntity> results = null;
		Map<String, JSONObject> objs = (Map<String, JSONObject>) obj;
		Map<String, AbstractPlaneInfoEntity> goentitys = Maps.newHashMap();// 去程航班Map
		Table<String, String, AbstractPlaneInfoEntity> retables = HashBasedTable.create();// 去程对应的回程航班Table

		try {

			// 循环解析各仓位。
			for (String cabinName : objs.keySet()) {
				JSONObject flights = objs.get(cabinName);

				String currency = flights.getJSONObject("Query").getJSONObject("UserInfo").getString("CurrencyId"); // 获取货币类型
				Map<String, JSONObject> itineraries = TianxunAdapterUtil.jsonToMap(flights.getJSONArray("Itineraries"));// 行程
				Map<String, JSONObject> agents = TianxunAdapterUtil.jsonToMap(flights.getJSONArray("Agents")); // 代理商
				Map<String, JSONObject> carriers = TianxunAdapterUtil.jsonToMap(flights.getJSONArray("Carriers")); // 航空公司
				Map<String, JSONObject> outboundItineraryLegs = TianxunAdapterUtil.jsonToMap(flights.getJSONArray("OutboundItineraryLegs")); // 航空公司
				Map<String, JSONObject> inboundItineraryLegs = TianxunAdapterUtil.jsonToMap(flights.getJSONArray("InboundItineraryLegs")); // 航空公司
				for (JSONObject flight : itineraries.values()) {
					String outkey = null, inkey = null;
					JSONObject detailInfo = null, option = null, item = null;
					Map<String, JSONObject> stations = null, outboundSegments = null, inboundSegments = null;
					AbstractPlaneInfoEntity planeInfo = null, replaneInfo = null;
					try {
						if (flight.get("detailInfo") == null) continue; // 没有详细信息不能解析 。
						outkey = flight.getString("OutboundLegId"); // 去程航班Key
						inkey = flight.getString("InboundLegId"); // 回程航班Key

						detailInfo = flight.getJSONObject("detailInfo");
						option = detailInfo.getJSONArray("Options").getJSONObject(0); // 航班信息

						Double price = option.getDouble("Price");
						if (price.doubleValue() == 0) continue; // 价格为0，不保存。

						stations = TianxunAdapterUtil.jsonToMap(detailInfo.getJSONArray("Stations"));
						outboundSegments = TianxunAdapterUtil.jsonToMap(detailInfo.getJSONArray("OutboundSegments"));// 去程
						inboundSegments = TianxunAdapterUtil.jsonToMap(detailInfo.getJSONArray("InboundSegments"));// 返程

						item = option.getJSONArray("Items").getJSONObject(0);// 目前只取一个代理信息

						// 过滤重复去程。
						planeInfo = goentitys.get(outkey);
						if (planeInfo == null) {
							JSONObject outbound = outboundItineraryLegs.get(outkey);
							Long outDutation = outbound.getLong("Duration");// 获取总航程时间
							List<JSONObject> outboundSegmentInfo = TianxunAdapterUtil.jsonToList(item.getJSONArray("OutboundSegmentInfo")); // 去程航班信息
							planeInfo = buildPlaneInfo(outboundSegmentInfo, outboundSegments, stations, carriers, currency, price, outDutation, false);// 去程
							goentitys.put(outkey, planeInfo);
						}

						// 往返时过滤重复回程航班。
						replaneInfo = retables.get(outkey, inkey);
						if (replaneInfo == null && taskQueue.getIsReturn() == 1) {
							JSONObject inbound = inboundItineraryLegs.get(inkey);
							Long inDutation = inbound.getLong("Duration");// 获取总航程时间
							List<JSONObject> inboundSegmentInfo = TianxunAdapterUtil.jsonToList(item.getJSONArray("InboundSegmentInfo"));
							replaneInfo = buildPlaneInfo(inboundSegmentInfo, inboundSegments, stations, carriers, currency, price, inDutation, true);// 回程
							retables.put(outkey, inkey, replaneInfo);
						}

						buildAgent(planeInfo, item.getString("AgentId"), price, agents);// 代理只设去程 。
						buildCabin(planeInfo, replaneInfo, cabinName, price);// 保存舱位信息和设置舱位关系

					} catch (Exception e) {
						logger.info("航班解析出错 ：" + e.getMessage());
						throw e;
					}finally{
						  outkey = null; inkey = null;
						  detailInfo = null; option = null; item = null;
						 stations = null;outboundSegments = null; inboundSegments = null;
						  planeInfo = null; replaneInfo = null;
					}
				}
			}

			// 组装回程航班
			if (taskQueue.getIsReturn() == 1) {
				for (String outkey : goentitys.keySet()) {
					DoublePlaneInfoEntity planeInfo = PlaneInfoEntityBuilder.getDoubleEntity(goentitys.get(outkey));
					for (String inkey : retables.row(outkey).keySet()) {
						ReturnDoublePlaneInfoEntity replaneInfo = PlaneInfoEntityBuilder.getReturnDoubleEntity(retables.get(outkey, inkey));
						planeInfo.getReturnPlaneInfos().add(replaneInfo);
					}
				}
			}
			results = ImmutableList.copyOf(goentitys.values());
			PlaneInfoEntityBuilder.buildLimitPrice(results); // 设置高低价格

		} finally {
			objs = null;
			goentitys = null;
			retables = null;
		}
		return results;
	}

	/** 组装航班实体。 */
	private AbstractPlaneInfoEntity buildPlaneInfo(List<JSONObject> transitMap, Map<String, JSONObject> boundSegments, Map<String, JSONObject> stations, Map<String, JSONObject> carriers, String currency, Double price, Long allDuration,
			Boolean isReturn) throws Exception {

		List<TransitEntity> transits = Lists.newArrayList();
		List<ReturnTransitEntity> retransits = Lists.newArrayList();
		AbstractPlaneInfoEntity planeInfo = null;
		Long totalTime = 0L;

		// 回程
		for (JSONObject bd : transitMap) {
			if (bd.get("Id") == null) continue; // 有些变异数据没有ID，咱不鸟

			String boundId = bd.getString("Id");
			JSONObject bound = boundSegments.get(boundId);
			JSONObject originStation = stations.get(bound.getString("OriginStation")); // 出发机场
			JSONObject destinationStation = stations.get(bound.getString("DestinationStation"));// 到达机场
			String departureDateTime = bound.get("DepartureDateTime") == null ? null : bound.getString("DepartureDateTime");// 出发时间
			String arrivalDateTime = bound.get("ArrivalDateTime") == null ? null : bound.getString("ArrivalDateTime"); // 到达时间
			Long duration = bound.get("Duration") == null ? 0 : bound.getLong("Duration"); // 飞行时长（分钟）
			// Long minimumConnectionTime = bound.get("MinimumConnectionTime") == null ? 0 : bound.getLong("MinimumConnectionTime"); // 中停时长（分钟）
			String flightNumber = bound.get("FlightNumber") == null ? "" : bound.getString("FlightNumber"); // 飞机尾号
			JSONObject carrier = carriers.get(bound.getString("MarketingCarrier")); // 航空公司信息

			totalTime += duration; // 叠加总飞行时长

			if (isReturn) {
				ReturnTransitEntity retransit = new ReturnTransitEntity();
				retransit.setCarrierKey(carrier.getString("Id"));
				retransit.setFlightNo(carrier.getString("Id") + flightNumber);
				retransit.setCarrierFullName(carrier.getString("Name"));
				retransit.setCarrierName(carrier.getString("Name"));
				retransit.setStartTime(TianxunAdapterUtil.convertDate(departureDateTime));
				retransit.setEndTime(TianxunAdapterUtil.convertDate(arrivalDateTime));
				retransit.setFromAirPort(originStation.getString("Code"));
				retransit.setFromAirPortName(originStation.getString("Name"));
				retransit.setToAirPort(destinationStation.getString("Code"));
				retransit.setToAirPortName(destinationStation.getString("Name"));
				retransits.add(retransit);
			} else {
				TransitEntity transit = new TransitEntity();
				transit.setCarrierKey(carrier.getString("Id"));
				transit.setFlightNo(carrier.getString("Id") + flightNumber);
				transit.setCarrierFullName(carrier.getString("Name"));
				transit.setCarrierName(carrier.getString("Name"));
				transit.setStartTime(TianxunAdapterUtil.convertDate(departureDateTime));
				transit.setEndTime(TianxunAdapterUtil.convertDate(arrivalDateTime));
				transit.setFromAirPort(originStation.getString("Code"));
				transit.setFromAirPortName(originStation.getString("Name"));
				transit.setToAirPort(destinationStation.getString("Code"));
				transit.setToAirPortName(destinationStation.getString("Name"));
				// transit.setStayTime(minimumConnectionTime * 60 * 1000); // 中转停留时间（毫秒）
				transits.add(transit);
			}
			String flightNo = filterFlightNo(carrier.getString("Id"));
			if (planeInfo == null) {
				if (isReturn) planeInfo = PlaneInfoEntityBuilder
						.buildPlaneInfo(taskQueue, flightNo, carrier.getString("Name"), carrier.getString("Name"), null, null, flightNo + flightNumber, null, null, null, null, ReturnDoublePlaneInfoEntity.class);
				else planeInfo = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, flightNo, carrier.getString("Name"), carrier.getString("Name"), null, null, flightNo + flightNumber, null, null, null, null);
			}
		}

		// 中转信息大于2条则有中转信息
		if (transits.size() > 1 || retransits.size() > 1) {
			Long stayTime = (allDuration - totalTime) * 60 * 1000;// 只处理一次中转的停留时间，目前没有办法获取中转停留时间。一段的是计算出来的。
			if (isReturn) {
				PlaneInfoEntityBuilder.getReturnDoubleEntity(planeInfo).getReturnTransits().addAll(retransits);
				if (retransits.size() <= 2) retransits.get(0).setStayTime(stayTime);
			} else {
				if (taskQueue.getIsReturn() == 1) PlaneInfoEntityBuilder.getDoubleEntity(planeInfo).getTransits().addAll(transits);
				else PlaneInfoEntityBuilder.getSingleEntity(planeInfo).getTransits().addAll(transits);
				if (transits.size() <= 2) transits.get(0).setStayTime(stayTime);
			}
		}

		Long flightDuration = allDuration * 60 * 1000;// 设置总时间 MS
		if (isReturn) {// 往返回程
			ReturnDoublePlaneInfoEntity replaneInfo = PlaneInfoEntityBuilder.getReturnDoubleEntity(planeInfo);
			replaneInfo.setFlightDuration(flightDuration);
			replaneInfo.setCurrency(currency);
			planeInfo.setStartTime(retransits.get(0).getStartTime());
			planeInfo.setEndTime(retransits.get(retransits.size() - 1).getEndTime());
		} else {
			if (taskQueue.getIsReturn() == 1) {// 往返去程
				DoublePlaneInfoEntity goplaneInfo = PlaneInfoEntityBuilder.getDoubleEntity(planeInfo);
				goplaneInfo.setFlightDuration(flightDuration);
				goplaneInfo.setCurrency(currency);
				goplaneInfo.setStartTime(transits.get(0).getStartTime());
				goplaneInfo.setEndTime(transits.get(transits.size() - 1).getEndTime());
			} else {// 单程去程
				SinglePlaneInfoEntity goplaneInfo = PlaneInfoEntityBuilder.getSingleEntity(planeInfo);
				goplaneInfo.setFlightDuration(flightDuration);
				goplaneInfo.setStartTime(transits.get(0).getStartTime());
				goplaneInfo.setEndTime(transits.get(transits.size() - 1).getEndTime());
			}
		}
		planeInfo.setSumLowestPrice(price);
		planeInfo.setSumHighestPrice(price);
		return planeInfo;
	}

	/** 组装代理。 */
	private void buildAgent(AbstractPlaneInfoEntity planeInfo, String agentId, Double price, Map<String, JSONObject> agents) {
		JSONObject agentJson = agents.get(agentId);
		AgentEntity agent = new AgentEntity();
		agent.setOriginalPrice(price);
		agent.setName(agentJson.getString("Name"));
		agent.setType(agentJson.getBoolean("IsCarrier") ? 0 : 1);
		agent.setPlaneInfoEntity(planeInfo);
		if (taskQueue.getIsReturn() == 1) {
			PlaneInfoEntityBuilder.getDoubleEntity(planeInfo).getAgents().add(agent);
		} else {
			PlaneInfoEntityBuilder.getSingleEntity(planeInfo).getAgents().add(agent);
		}
	}

	/** 组装舱位并设置舱位关系。 */
	private void buildCabin(AbstractPlaneInfoEntity planeInfo, AbstractPlaneInfoEntity replaneInfo, String cabinName, Double price) throws Exception {
		// 往返舱位
		if (taskQueue.getIsReturn() == 1) {

			// 保存往返去程舱位。
			CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, CABINS.get(cabinName), "", null, null, null, null, CabinEntity.class);
			PlaneInfoEntityBuilder.getDoubleEntity(planeInfo).getCabins().add(cabin);

			// 保存往返回程舱位。
			ReturnCabinEntity recabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, CABINS.get(cabinName), "", null, null, null, null, ReturnCabinEntity.class);
			PlaneInfoEntityBuilder.getReturnDoubleEntity(replaneInfo).getReturnCabins().add(recabin);

			// 保存去回程舱位关系。
			CabinRelationEntity relation = new CabinRelationEntity();
			relation.setCabinId(cabin.getId());
			relation.setReturnCabinId(recabin.getId());
			relation.setPlaneInfoEntity(planeInfo);
			relation.setTotalFullPrice(price); // 只有一个总价
			PlaneInfoEntityBuilder.getDoubleEntity(planeInfo).getCabinRelations().add(relation);
		} else {
			// 单程舱位。
			CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, "", null, null, null, null, CabinEntity.class);
			PlaneInfoEntityBuilder.getSingleEntity(planeInfo).getCabins().add(cabin);
		}
	}

	/** ******************************** FETCH DATA*********************************************** */

	/** 抓取国际往返数据。 */
	public Object fetchInterRound() throws Exception {
		Map<String, JSONObject> results = Maps.newHashMap();
		String page = null;
		JSONObject flights = null;
		String sessionId = null;

		page = setCookie(COOKIE_RETRY); // 设置Cookie信息。

		for (String cabin : CABINS.keySet()) {
			page = getPageData(sessionId, cabin, CABIN_RETRY);
			validateFetchData(page);

			flights = JSONObject.fromObject(page);
			sessionId = flights.getString("SessionKey");
			JSONArray itineraries = flights.getJSONArray("Itineraries");

			logger.info(String.format("舱位[%s]数量为[%s],现在抓取详细数据...", cabin, itineraries.size()));
			for (int index = 0; index < itineraries.size(); index++) {
				JSONObject json = itineraries.getJSONObject(index);
				String outkey = json.get("OutboundLegId").toString();
				String inkey = taskQueue.getIsReturn() == 1 ? json.get("InboundLegId").toString() : "";
				String detailString = null;

				// 需要过滤航空公司
				if (IS_FILTER_CARRIER && !TianxunCarrierFilter.checkCarrier(taskQueue, outkey, inkey, flights)) { // 过滤航班
					continue;
				}

				// 需要过滤多次中转
				if (MAX_TRANSIT >= 0 && !TianxunCarrierFilter.checkMultiTransit(taskQueue, outkey, inkey, flights, MAX_TRANSIT)) { // 过滤航班
					continue;
				}

				// 获取详细数据。
				detailString = getDetailData(sessionId, outkey, inkey, DETAIL_RETRY);
				if (detailString != null) {
					json.accumulate("detailInfo", detailString);// 把详细数据组装到Json对象中去。
				}
			}
			super.setLenghtCount(flights.toString().getBytes().length);
			results.put(cabin, flights);
		}
		return results;
	}

	/** 获取数据。 */
	private String getPageData(String sessionId, String cabin, Integer retry) throws Exception {
		HttpPost post = null;
		String page = null;
		try {
			Map<String, String> params = Maps.newHashMap();
			params.put("CabinClass", cabin);
			params.put("OriginPlace", taskQueue.getFromCity());
			params.put("DestinationPlace", taskQueue.getToCity());
			params.put("OutboundDate", taskQueue.getFlightDate());
			if (taskQueue.getIsReturn() == 1) params.put("InboundDate", taskQueue.getReturnGrabDate());
			params.put("JourneyModes", "flight");
			params.put("MergeCodeshares", "false");
			params.put("Passengers.Adults", "1");
			params.put("Passengers.Children", "0");
			params.put("Passengers.Infants", "0");
			params.put("RequestId", java.util.UUID.randomUUID().toString());
			params.put("SkipMixedAirport", "false");
			params.put("UserInfo.ChannelId", "transportfunnel");
			params.put("UserInfo.CountryId", "CN");
			params.put("UserInfo.CurrencyId", "cny");
			params.put("UserInfo.LanguageId", "ZH");

			String url = DATAURL.concat(sessionId == null ? "" : sessionId).concat("?use204=true");
			post = getBasePost(url.toString(), params);
			post.setHeader("Host", "www.tianxun.cn");
			post.setHeader("Cache-Control", "no-cache");
			page = excuteRequest(super.getHttpClient(), post, true);
			super.appendPageContents(page);
		} catch (Exception e) {
			rollbackProxyIp(false);
			logger.info(String.format("获取舱位[%s]数据异常,现在更换IP[%s]重试 ", super.proxyIp.getIp(), e.getMessage()));
			if (retry-- > 0) {
				rollbackProxyIp(false);
				super.switchProxyipByHttClient();// 更换IP重试
				this.getPageData(sessionId, cabin, retry); // 重试
			}
			if (cabin.equals("Economy")) throw new Exception(String.format("获取舱位[%s]数据失败。!", cabin));
			logger.info(String.format("获取舱位[%s]数据失败，该放手时就放手！ "));
		} finally {
			if (post != null) post.releaseConnection();
		}
		return page;
	}

	/** 设置 Cookie 信息。 */
	private String setCookie(Integer retry) throws Exception {
		HttpGet get = null;
		String page = null;
		try {
			get = new HttpGet("http://www.tianxun.cn?t=" + System.currentTimeMillis());
			get.setHeader("Host", "www.tianxun.cn");
			excuteRequest(super.getHttpClient(), get, false);
			String fromCity = taskQueue.getFromCity().toLowerCase();
			String toCity = taskQueue.getToCity().toLowerCase();
			String flightDate = taskQueue.getFlightDate().substring(2).replace("-", "");
			String returnDate = taskQueue.getReturnGrabDate().substring(2).replace("-", "");
			String year = taskQueue.getFlightDate().substring(0, 4);
			String month = PlaneInfoEntityBuilder.getEnMonth(taskQueue.getFlightDate());
			if (taskQueue.getIsReturn() == 1) get = new HttpGet(String.format("%s%s/%s/%s/%s/airfares-from-%s-to-%s-in-%s-%s.html?rtn=%s", BASEURL, fromCity, toCity, flightDate, returnDate, taskQueue.getFromCity(), taskQueue.getToCity(), month,
					year, taskQueue.getIsReturn()));
			else get = new HttpGet(String.format("%s%s/%s/%s/airfares-from-%s-to-%s-in-%s-%s.html?rtn=%s", BASEURL, fromCity, toCity, flightDate, taskQueue.getFromCity(), taskQueue.getToCity(), month, year, taskQueue.getIsReturn()));
			get.setHeader("Host", "www.tianxun.cn");
			get.setHeader("Referer", "http://www.tianxun.cn/");
			page = excuteRequest(getHttpClient(), get, true);
			super.setLenghtCount(page.length());// 统计流量
		} catch (Exception e) {
			if (retry-- > 0) {
				logger.info(String.format("设置Cookie信息失败，现在更换新IP[%s]重试 :%s", super.proxyIp.getIp(), e.getMessage()));
				rollbackProxyIp(false);
				super.switchProxyipByHttClient();// 更换IP重试
				this.setCookie(retry); // 重试
			}
			throw new Exception("设置Cookie信息失败!");
		} finally {
			if (get != null) get.releaseConnection();
		}
		return page;
	}

	/** 获取航班详细数据。 */
	private String getDetailData(String sessionId, String outboundId, String inboundId, Integer retry) throws Exception {
		HttpRequestBase request = null;
		String detailUrl = null;
		String page = null;
		try {
			if (taskQueue.getIsReturn() == 1) detailUrl = String.format("%s%s/booking/%s;%s", DATAURL, sessionId, outboundId, inboundId);
			else detailUrl = String.format("%s%s/booking/%s", DATAURL, sessionId, outboundId);
			request = new HttpGet(detailUrl);
			page = excuteRequest(request, FETCH_SLEEP_TIME);
			// 如果没有获取到航班号。
			if (!RegHtmlUtil.regMathcher(page, "FlightNumber") && flightNotNULL) { // 没有获取到航班号
				logger.error(String.format("[%s-%s]航班号获取失败!", outboundId, inboundId));
				if (flightNotNULL) return null; // 不保存没有航班号的航班 。
			}
		} catch (Exception e) {
			if (retry-- > 0) {
				logger.info(String.format("获取航班详细数据异常，现在更换新IP[%s]试试 :%s", super.proxyIp.getIp(), e.getMessage()));
				rollbackProxyIp(false);
				super.switchProxyipByHttClient();// 更换IP重试
				this.getDetailData(sessionId, outboundId, inboundId, retry); // 重试
			}
			logger.info(String.format("获取航班详细数据失败,该放手时就放手！"));
		} finally {
			if (request != null) request.releaseConnection();
		}
		return page;
	}

	/** 日期处理。 */
	private void validateFetchData(String page) throws Exception {
		if (Strings.isEmpty(page)) {
			throw new Exception("抓取的数据为空");
		}
		if (!RegHtmlUtil.regMathcher(page, "SessionKey")) {
			logger.error("没有找到相应的航班 ！");
			throw new FlightInfoNotFoundException("没有找到相应的航班 ！");
		}
	}

	private String filterFlightNo(String flightNo) {
		if (flightNo != null && flightNo.equals("60")) return "EY"; // 目前只发现阿拉提航空需要特殊处理。
		return flightNo;
	}
}